/*+***********************************************************************************
 Filename: 04_arduino_step01\src\top.v
 Description: demo a simple mcu which supports Arduino framework.

 Modification:
   2022.10.12 Creation   H.Zheng
   2022.11.18 add HWLoader, change ram and rom to dualport  H.Zheng

Copyright (C) 2022  Zheng Hui (hzheng@gzhu.edu.cn)

License: MulanPSL-2.0

***********************************************************************************-*/

module top
(
    input wire [1:0] button,
    input wire sys_clk,
    output wire [5:0] led,
    input wire uart0_rxd,
    output wire uart0_txd
);
    //PLL
    wire clk_200MHz;
    Gowin_rPLL m_pll(
        .clkout(clk_200MHz), //output clkout
        .clkin(sys_clk) //input clkin
    );

    //
    wire reset_n = button[1];

    //core clk
    reg [24:0] counter;

    always @(posedge sys_clk or negedge reset_n) begin
        if (~reset_n) begin
            counter <= 0;
        end
        else begin
            counter <= counter + 1'b1;
        end
    end

    //core
//    wire core_clk = counter[24];
    wire core_clk = counter[20];
    wire [31:0] ibus_addr;
    wire ibus_re;
    wire [31:0] instruction;
    wire [31:0] monitor_port;
    wire ram_ce;
    wire ram_wre;
//    wire [8:0] ram_addr;
    wire [9:0] ram_addr;
    wire [31:0] ram_data_in;
    wire [31:0] ram_data_out;
    //
    wire peripheral_ce;
    wire peripheral_wre;
    wire [8:0] peripheral_addr;
    wire [31:0] peripheral_data_in;
    wire [31:0] peripheral_data_out;

    wire core_RST_N;
    core m_core (
        .core_clk(core_clk),
        .reset_n(core_RST_N),
        .ibus_addr(ibus_addr),
        .ibus_re(ibus_re),
        .instruction(instruction),
        .ram_ce(ram_ce),
        .ram_wre(ram_wre),
        .ram_addr(ram_addr),
        .ram_data_in(ram_data_in),
        .ram_data_out(ram_data_out),
        //
        .peripheral_ce(peripheral_ce),
        .peripheral_wre(peripheral_wre),
        .peripheral_addr(peripheral_addr),
        .peripheral_data_in(peripheral_data_in),
        .peripheral_data_out(peripheral_data_out),
        //
        .monitor_port(monitor_port)
    );





    //peripherals
    wire [5:0] peri_led;
    wire peri_txd;    

    peripheral m_peripheral(
        .clk(sys_clk),
        .reset_n(reset_n),
        .button(button[0]),
//        .led(led),
        .led(peri_led),
        .rxd(uart0_rxd),
        .txd(peri_txd),
        //
        .ce(peripheral_ce),
        .wre(peripheral_wre),
        .addr(peripheral_addr),
        .data_in(peripheral_data_out),
        .data_out(peripheral_data_in)
    );


    //Hardware Loader
	wire [31:0] debug_rom_doa;
	wire [31:0] debug_ram_doa;
	wire [31:0] debug_doa;
	wire [31:0] debug_rom_dia;
	wire [21:0] debug_rom_addra_rd;	
	wire [21:0] debug_rom_addra_wr;	
	wire debug_rom_rea;
	wire debug_rom_wea;
    wire debug_TXD;	
    wire debug_txd_sel;	
    wire HW_Loader_Reset;		

    debug_coprocessor_wrapper #(.BAUD_PERIOD (234)) hwloader(    
        .clk(sys_clk),  //27MHz
        .reset_n(reset_n),    
        .RXD(uart0_rxd),
        .TXD(debug_TXD),     
        .debug_uart_tx_sel_ocd1_cpu0(debug_txd_sel),   
        .pram_read_enable_in(1'b1),    
        .pram_read_enable_out(debug_rom_rea),    
        .pram_read_addr_out(debug_rom_addra_rd),    
        .pram_read_data_in(debug_doa),    
        .pram_write_enable_out(debug_rom_wea),    
        .pram_write_addr_out(debug_rom_addra_wr),     
        .pram_write_data_out(debug_rom_dia),    
        .cpu_reset(HW_Loader_Reset)
    );  

    assign core_RST_N = (~HW_Loader_Reset) & reset_n;	
    assign uart0_txd = debug_txd_sel?debug_TXD:peri_txd;    

//    assign debug_rom_addra = debug_rom_wea ? debug_rom_addra_wr : debug_rom_addra_rd;    
//    assign debug_rom_cea = debug_rom_rea|debug_rom_wea;    
//rom address in bytes = [0x????2000-0x????2fff], in words [0x800-bff]
//ram address in bytes = [0x????0400-0x????13ff], in words [0x100-4ff]
    wire [21:0] debug_rom_addra = debug_rom_wea ? debug_rom_addra_wr : debug_rom_addra_rd;    
    wire debug_rom_cea = (debug_rom_addra[13:11]==3'b001)&(debug_rom_rea|debug_rom_wea);    
//    wire debug_rom_cea = debug_rom_rea|debug_rom_wea;    
    wire debug_ram_cea = (debug_rom_addra[13:11]==3'b000)&(debug_rom_rea|debug_rom_wea); 
//    wire debug_ram_cea = 1'b0; 
    assign debug_doa = debug_rom_cea ? debug_rom_doa :
                         debug_ram_cea ? debug_ram_doa : 32'b0;
//    assign debug_doa = debug_rom_doa;

    //IROM
    //address and control input from core
    //data output to core

/*
    Gowin_pROM I_ROM(
        .dout(instruction), //output [31:0] dout
        .clk(clk_200MHz), //input clk
        .oce(ibus_re), //input oce
        .ce(1'b1), //input ce
        .reset(~reset_n), //input reset
        .ad(ibus_addr[2:0]) //input [2:0] ad
    );
*/
    Gowin_DPB I_ROM(
        .clka(clk_200MHz), //input clka
        .ocea(1'b1), //input ocea
        .cea(debug_rom_cea), //input cea
        .reseta(~reset_n), //input reseta
        .wrea(debug_rom_wea), //input wrea
        .ada(debug_rom_addra[9:0]), //input [9:0] ada
        .douta(debug_rom_doa), //output [31:0] douta
        .dina(debug_rom_dia), //input [31:0] dina

        .clkb(clk_200MHz), //input clkb
        .oceb(ibus_re), //input oceb
        .ceb(1'b1), //input ceb
        .resetb(~reset_n), //input resetb
        .wreb(1'b0), //input wreb
        .adb(ibus_addr[9:0]), //input [9:0] adb
        .doutb(instruction), //output [31:0] doutb
        .dinb(32'b0) //input [31:0] dinb
    );  

    //data RAM
/*
    Gowin_SP D_RAM(
        .dout(ram_data_in), //output [31:0] dout
        .clk(clk_200MHz), //input clk
//        .clk(sys_clk), //input clk
        .oce(1'b1), //input oce
        .ce(ram_ce), //input ce
        .reset(~reset_n), //input reset
        .wre(ram_wre), //input wre
        .ad(ram_addr), //input [8:0] ad
        .din(ram_data_out) //input [31:0] din
    );
*/
    Gowin_DPB D_RAM(
        .clka(clk_200MHz), //input clka
        .ocea(1'b1), //input ocea
        .cea(debug_ram_cea), //input cea
        .reseta(~reset_n), //input reseta
        .wrea(debug_rom_wea), //input wrea
        .ada(debug_rom_addra[9:0]), //input [9:0] ada
        .douta(debug_ram_doa), //output [31:0] douta
        .dina(debug_rom_dia), //input [31:0] dina

        .clkb(clk_200MHz), //input clkb
        .oceb(1'b1), //input oceb
        .ceb(ram_ce), //input ceb
        .resetb(~reset_n), //input resetb
        .wreb(ram_wre), //input wreb
        .adb(ram_addr), //input [9:0] adb
        .doutb(ram_data_in), //output [31:0] doutb
        .dinb(ram_data_out) //input [31:0] dinb
    );  


    //display

//    assign led = {~debug_txd_sel, ~monitor_port[5:3], peri_led[1:0]};
//    assign led = ~monitor_port[5:0];
    assign led = peri_led;

endmodule